package com.arpa.wms.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.arpa.ntocc.common.common.cache.PartyCache;
import com.arpa.ntocc.common.common.exception.ServiceException;
import com.arpa.ntocc.common.common.util.UserUtil;
import com.arpa.ntocc.common.domain.vo.TreeData;
import com.arpa.wms.cache.GoodsCache;
import com.arpa.wms.cache.GoodsTypeCache;
import com.arpa.wms.common.util.TreeUtils;
import com.arpa.wms.common.util.UUIDUtil;
import com.arpa.wms.domain.consts.CommonConst;
import com.arpa.wms.domain.consts.NumConst;
import com.arpa.wms.domain.entity.GoodsType;
import com.arpa.wms.domain.vo.GoodsTypeVOList;
import com.arpa.wms.domain.vo.TreeNodeVO;
import com.arpa.wms.mapper.GoodsTypeMapper;
import com.arpa.wms.service.IGoodsTypeService;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.collect.Lists;
import com.qimen.api.request.ItemsSynchronizeRequest;
import com.qimen.api.request.SingleitemSynchronizeRequest;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import static com.arpa.wms.domain.consts.CodeConst.CODE;
import static com.arpa.wms.domain.consts.CodeConst.PARENT_CODE;
import static com.arpa.wms.domain.consts.NumConst.NUM_ZERO;
import static com.arpa.wms.domain.consts.TipConst.GOODS_TYPE_HAVE_CHILDREN;
import static com.arpa.wms.domain.consts.TipConst.GOODS_TYPE_HAVE_GOODS;

/**
 * @author: Allen chen
 * @Date 2020/8/15
 * @Description:
 */
@Service
@Log4j2
public class GoodsTypeServiceImpl extends ServiceImpl<GoodsTypeMapper, GoodsType> implements IGoodsTypeService {

    private final PartyCache partyCache;
    private final GoodsTypeCache goodsTypeCache;
    @Autowired
    private GoodsCache goodsCache;

    public GoodsTypeServiceImpl(PartyCache partyCache, GoodsTypeCache goodsTypeCache) {
        this.partyCache = partyCache;
        this.goodsTypeCache = goodsTypeCache;
    }

    @Override
    public GoodsTypeVOList queryList(GoodsType goodsType) {

        List<GoodsType> goodsTypes = baseMapper.queryList(goodsType);
        GoodsTypeVOList response = new GoodsTypeVOList();
        List<TreeNodeVO> list = goodsTypes.stream().map(info -> {
            TreeNodeVO vo = new TreeNodeVO();
            BeanUtils.copyProperties(info, vo);
            return vo;
        }).sorted(Comparator.comparingInt(TreeNodeVO::getSort)).collect(Collectors.toList());
        List<TreeNodeVO> treeNodeVoS = TreeUtils.toTree(list);
        response.setList(treeNodeVoS);
        return response;
    }

    @Override
    @Transactional(rollbackFor={Exception.class})
    public boolean save(GoodsType goodsType) {

        GoodsType one = baseMapper.queryOneByName(goodsType);
        if(null != one){throw new ServiceException("类别名称已存在!");}
        GoodsType type = baseMapper.queryOneByTypeCode(goodsType);
        if(null != type){throw new ServiceException("类别编码已存在!");}
        // TODO: 2020/8/21
        goodsType.setCode(UUIDUtil.getUUID32());
        goodsType.setCreatedBy(UserUtil.getCode());
        goodsTypeCache.flushAllCacheByGroupCode(UserUtil.getBranchCode());
        goodsTypeCache.flushAllCacheByShipmentCode(goodsType.getShipmentCode());
        return super.save(goodsType);
    }

    @Override
    public int updateByCode(GoodsType goodsType) {

        if(StrUtil.isBlank(goodsType.getModifiedBy())){
            goodsType.setModifiedBy(UserUtil.getCode());
        }
        goodsTypeCache.flushCache(goodsType.getCode());
        return baseMapper.update(goodsType,new QueryWrapper<GoodsType>().lambda().eq(GoodsType::getCode,goodsType.getCode()));
    }

    @Override
    @Transactional(rollbackFor={Exception.class})
    public int delGoodsTypeWithChild(GoodsType goodsType) {
        goodsType.setModifiedBy(UserUtil.getCode());
        return baseMapper.delGoodsTypeWithChild(goodsType);
    }

    @Override
    public List<GoodsType> getListByCodes(List<String> codes) {

        return baseMapper.getListByCodes(codes);
    }

    @Override
    public List<GoodsType> getByCodeOrParentCode(String code,String shipmentCode) {
        List<GoodsType> queryList = baseMapper.getByCodeOrParentCode(code,shipmentCode);
        return queryList;
    }

    /**
     * 获取货主商品分类
     *
     * @param shipmentCode
     * @return
     */
    @Override
    public List<TreeData> findByShipmentCode(String shipmentCode) {
        List<Map<String,Object>> goodsTypes = goodsTypeCache.getAllByShipmentCode(shipmentCode);
        List<TreeData> treeData = goodsTypes.stream()
                .filter(r -> MapUtil.getInt(r, CommonConst.LEVEL) == 1).map(
                        r -> {
                            TreeData treeDataVO = new TreeData();
                            treeDataVO.setId(MapUtil.getStr(r,"id"));
                            treeDataVO.setValue(MapUtil.getStr(r,"code"));
                            if(StringUtils.isNotEmpty(MapUtil.getStr(r,CommonConst.TYPE_CODE))){
                                treeDataVO.setLabel(MapUtil.getStr(r,CommonConst.NAME)+"("+MapUtil.getStr(r,CommonConst.TYPE_CODE)+")");
                            }else {
                                treeDataVO.setLabel(MapUtil.getStr(r,CommonConst.NAME));
                            }
                            treeDataVO.setLevel(MapUtil.getInt(r,CommonConst.LEVEL));
                            treeDataVO.setSpread(true);
                            treeDataVO.setSort(MapUtil.getInt(r,"sort"));
                            treeDataVO.setChildren(selectChildren(MapUtil.getStr(r,"code"), goodsTypes));
                            return treeDataVO;
                        }
                ).sorted(Comparator.comparingInt(TreeData::getSort)).collect(Collectors.toList());

        return treeData;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void removeByCode(String code) {
        GoodsType goodsType = baseMapper.selectOne(new QueryWrapper<GoodsType>().lambda().eq(GoodsType::getCode, code));
        List<Map<String, Object>> goodsTypeCacheByShipmentCode = goodsTypeCache.getAllByShipmentCode(goodsType.getShipmentCode());
        // 判断是否有下一级分类
        List<TreeData> children = selectChildren(code, goodsTypeCacheByShipmentCode);
        if(CollUtil.isNotEmpty(children)) {
            throw new ServiceException(GOODS_TYPE_HAVE_CHILDREN);
        }
        // 判断分类下是否有商品
        List<Map<String, Object>> goodsCacheByShipmentCode = goodsCache.getAllByShipmentCode(goodsType.getShipmentCode());
        List<Map<String, Object>> goodsCacheList = goodsCacheByShipmentCode.stream().filter(item -> code.equals(item.get(CommonConst.TYPE_CODE))).collect(Collectors.toList());
        if(CollUtil.isNotEmpty(goodsCacheList)) {
            throw new ServiceException(GOODS_TYPE_HAVE_GOODS);
        }
        baseMapper.delete(new UpdateWrapper<GoodsType>().lambda().eq(GoodsType::getCode, code));
        goodsTypeCache.flushAllCacheByShipmentCode(goodsType.getShipmentCode());
        goodsTypeCache.flushCache(code);
    }

    @Override
    public void listByShipmentCode(List<String> typeCodes, String parentCode, String shipmentCode) {


        List<Map<String,Object>> goodsTypes = goodsTypeCache.getAllByShipmentCode(shipmentCode);
        goodsTypes.stream().forEach(item -> {
            if (parentCode.equals(item.get(PARENT_CODE))) {
                typeCodes.add((String) item.get(CODE));
                listByShipmentCode(typeCodes, (String) item.get(CODE), shipmentCode);
            }
        });
    }

    /**
     * 查询商品分类下的子节点数量
     * @param parentCode
     * @return
     */
    @Override
    public int countChildren(String parentCode) {
        return this.count(new QueryWrapper<GoodsType>().lambda().eq(GoodsType::getParentCode, parentCode).eq(GoodsType::getDeleted, NUM_ZERO));
    }

    /**
     * 获取子级
     * 递归构建树
     *
     * @param parentGoodsTypeId
     * @return
     */
    private List<TreeData> selectChildren(String parentGoodsTypeId, final List<Map<String,Object>> goodsTypeCacheDoS) {

        List<TreeData> treeData = goodsTypeCacheDoS.stream()
                .filter(r -> StringUtils.equalsIgnoreCase(MapUtil.getStr(r,"parentCode"), parentGoodsTypeId))
                .map(r -> {
                    TreeData treeData1 = new TreeData();

                    treeData1.setId(MapUtil.getStr(r,"id"));
                    treeData1.setValue(MapUtil.getStr(r,"code"));
                    if(StringUtils.isNotEmpty(MapUtil.getStr(r,CommonConst.TYPE_CODE))){
                        treeData1.setLabel(MapUtil.getStr(r,CommonConst.NAME)+"("+MapUtil.getStr(r,CommonConst.TYPE_CODE)+")");
                    }else {
                        treeData1.setLabel(MapUtil.getStr(r,CommonConst.NAME));
                    }
                    treeData1.setLabel(MapUtil.getStr(r,CommonConst.NAME));
                    treeData1.setLevel(MapUtil.getInt(r,CommonConst.LEVEL));
                    treeData1.setSpread(true);
                    treeData1.setSort(MapUtil.getInt(r,"sort"));
                    treeData1.setTypeCode(MapUtil.getStr(r,CommonConst.TYPE_CODE));

                    if (selectChildren(MapUtil.getStr(r,"code"), goodsTypeCacheDoS).size() == 0) {
                        return treeData1;
                    } else {
                        treeData1.setChildren(selectChildren(MapUtil.getStr(r,"code"), goodsTypeCacheDoS));
                    }
                    return treeData1;
                }).sorted(Comparator.comparingInt(TreeData::getSort)).collect(Collectors.toList());
        return treeData;
    }

    /**
     * 批量同步商品时，根据操作类型判断是插入还是更新
     *
     * @param request
     */
    @Override
    @Transactional(rollbackFor = {Exception.class})
    public void batchGoodsTypeSave(ItemsSynchronizeRequest request){
        String shipmentCode = request.getOwnerCode();
        List<ItemsSynchronizeRequest.Item> items = request.getItems();
        List<GoodsType> goodsTypeList = Lists.newArrayList();
        for (ItemsSynchronizeRequest.Item item : items) {
            String goodsTypeCode = item.getCategoryId();
            GoodsType oraginGoodsType = this.getOne(new QueryWrapper<GoodsType>().lambda().eq(GoodsType::getCode, goodsTypeCode).eq(GoodsType::getDeleted, NumConst.NUM_ZERO));
            if (ObjectUtil.isNull(oraginGoodsType)) {
                GoodsType goodsType = new GoodsType();
                goodsType.setCode(goodsTypeCode);
                goodsType.setShipmentCode(shipmentCode);
                goodsType.setParentCode("0");
                goodsType.setGmtCreated(LocalDateTime.now());
                goodsType.setCreatedBy(UserUtil.getCode());
                goodsType.setCreatedName(partyCache.translate(UserUtil.getCode()));
                goodsTypeList.add(goodsType);
            }
        }
        // 拼接sql，批量执行新增操作
        importBatchSave(goodsTypeList);
    }

    /**
     * 批量同步商品时，根据操作类型判断是插入还是更新
     *
     * @param request
     */
    @Override
    @Transactional(rollbackFor = {Exception.class})
    public void singleGoodsTypeSave(SingleitemSynchronizeRequest request){
        log.info("《《《《《 同步商品类型开始 4》》》》》");
        String shipmentCode = request.getOwnerCode();
        String goodsTypeCode = request.getItem().getCategoryId();
        GoodsType oraginGoodsType = this.getOne(new QueryWrapper<GoodsType>().lambda().eq(GoodsType::getCode, goodsTypeCode).eq(GoodsType::getDeleted, NumConst.NUM_ZERO));
        if (ObjectUtil.isNull(oraginGoodsType)) {
            GoodsType goodsType = new GoodsType();
            goodsType.setCode(goodsTypeCode);
            goodsType.setShipmentCode(shipmentCode);
            goodsType.setParentCode("0");
            goodsType.setGmtCreated(LocalDateTime.now());
            goodsType.setCreatedBy(UserUtil.getCode());
            goodsType.setCreatedName(partyCache.translate(UserUtil.getCode()));
            this.save(goodsType);
        }
        log.info("《《《《《 同步商品类型结束 4》》》》》");
    }

    /**
     * 同步-批量保存操作
     * @param goodsTypeList
     * @return
     */
    private int importBatchSave(List<GoodsType> goodsTypeList) {
        StringBuilder data = new StringBuilder();
        data.append("INSERT INTO `base_goods_type`(`id`, `code`,`group_code`, `shipment_code`,`parent_code`, "
                + " `gmt_created`,`created_by`, `created_name`) values ");
        String sql = goodsTypeList.stream().map(e -> makeInsertValueStr(e)).collect(Collectors.joining(","));
        data.append(sql);
        return this.baseMapper.execSql(data.toString());
    }

    /**
     * 同步-批量插入生成sql
     * @param goodsType
     * @return
     */
    private StringBuilder makeInsertValueStr(final GoodsType goodsType) {
        StringBuilder data = new StringBuilder();
        data.append("(");
        // id
        data.append("default, ");
        // code
        data.append("'").append(goodsType.getCode()).append("', ");
        // group_code
        data.append("'").append(UserUtil.getBranchCode()).append("', ");
        // shipment_code
        data.append("'").append(ObjectUtil.defaultIfNull(goodsType.getShipmentCode(), StrUtil.EMPTY)).append("', ");
        // parent_code
        data.append("'").append(goodsType.getParentCode()).append("', ");
        // gmt_created
        data.append("'").append(ObjectUtil.defaultIfNull(goodsType.getGmtCreated(), LocalDateTime.now())).append("', ");
        // created_by
        data.append("'").append(ObjectUtil.defaultIfNull(goodsType.getCreatedBy(), StrUtil.EMPTY)).append("', ");
        // created_name
        data.append("'").append(ObjectUtil.defaultIfNull(goodsType.getCreatedName(), StrUtil.EMPTY)).append("' ");
        data.append(")");
        return data;
    }

}
